package com.tobeface.wechat.web;

import com.tobeface.modules.lang.*;
import com.tobeface.wechat.core.WeChatSettings;
import com.tobeface.wechat.core.timestamp.WeChatTimestampService;
import com.tobeface.wechat.message.*;
import com.tobeface.wechat.message.annotation.DefaultMessageHandler;
import com.tobeface.wechat.message.WeChatMessageVerifyService;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

/**
 * @author loudyn
 */
public class WeChatFilter implements Filter {

    private static final String WECHAT_MESSAGE_CONTENT_TYPE = "text/xml";

    private static final String APPKEY_PARAM = "app-key";
    private static final String APPSECRET_PARAM = "app-secret";
    private static final String APPURL_PARAM = "app-url";
    private static final String APPTOKEN_PARAM = "app-token";
    private static final String SCAN_BASE_PKG = "scan-base-package";

    private static final String VERIFY_SERVICE_TYPE = "verify-service-type";
    private static final String TIMESTAMP_SERVICE_TYPE = "timestamp-service-type";
    private static final String MSG_TRACING_SERVICE_TYPE = "message-tracing-service-type";

    private WeChatMessageDispatcher weChatMessageDispatcher;

    @Override
    public void init(FilterConfig config) throws ServletException {

        WeChatSettings settings = buildSettings(config);
        List<WeChatMessageHandler> messageHandlers = buildMessageHandlers(config);
        List<WeChatMessageHandler> notDefaultHandlers = Langs.filter(messageHandlers, new Predicate<WeChatMessageHandler>() {
            @Override
            public boolean apply(WeChatMessageHandler input) {
                return !input.getClass().isAnnotationPresent(DefaultMessageHandler.class);
            }
        });

        WeChatMessageDispatcher weChatMessageDispatcher = new DefaultWeChatMessageDispatcher(settings, notDefaultHandlers);
        if (hasVerifyServiceProvider(config)) {
            configVerifyService(weChatMessageDispatcher, config);
        }
        if (hasTimestampServiceProvider(config)) {
            configTimestampService(weChatMessageDispatcher, config);
        }
        if (hasMessageTracingServiceProvider(config)) {
            configMessageTracingService(weChatMessageDispatcher, config);
        }
        if (hasDefaultMessageHandler(messageHandlers)) {
            configDefaultMessageHandler(weChatMessageDispatcher, messageHandlers);
        }

        this.weChatMessageDispatcher = weChatMessageDispatcher;
    }

    private WeChatSettings buildSettings(FilterConfig config) {
        String appKey = config.getInitParameter(APPKEY_PARAM);
        String appSecret = config.getInitParameter(APPSECRET_PARAM);
        String appUrl = config.getInitParameter(APPURL_PARAM);
        String appToken = config.getInitParameter(APPTOKEN_PARAM);
        return WeChatSettings.newBuilder().appKey(appKey).appSecret(appSecret).appUrl(appUrl).appToken(appToken).build();
    }

    private boolean hasVerifyServiceProvider(FilterConfig config) {
        return !Strings.isBlank(config.getInitParameter(VERIFY_SERVICE_TYPE));
    }

    private void configVerifyService(WeChatMessageDispatcher weChatMessageDispatcher,
                                     FilterConfig config) {

        String serviceType = config.getInitParameter(VERIFY_SERVICE_TYPE);
        Ghost<?> ghost = Ghost.me(serviceType);
        ((DefaultWeChatMessageDispatcher) weChatMessageDispatcher).setMessageVerifyService((WeChatMessageVerifyService) ghost.born());
    }

    private boolean hasTimestampServiceProvider(FilterConfig config) {
        return !Strings.isBlank(config.getInitParameter(TIMESTAMP_SERVICE_TYPE));
    }

    private void configTimestampService(WeChatMessageDispatcher weChatMessageDispatcher,
                                        FilterConfig config) {
        String serviceType = config.getInitParameter(TIMESTAMP_SERVICE_TYPE);
        Ghost<?> ghost = Ghost.me(serviceType);
        ((DefaultWeChatMessageDispatcher) weChatMessageDispatcher).setTimestampService((WeChatTimestampService) ghost.born());
    }

    private boolean hasMessageTracingServiceProvider(FilterConfig config) {
        return !Strings.isBlank(config.getInitParameter(MSG_TRACING_SERVICE_TYPE));
    }

    private void configMessageTracingService(WeChatMessageDispatcher weChatMessageDispatcher,
                                             FilterConfig config) {

        String serviceType = config.getInitParameter(MSG_TRACING_SERVICE_TYPE);
        Ghost<?> ghost = Ghost.me(serviceType);
        ((DefaultWeChatMessageDispatcher) weChatMessageDispatcher).setMessageTracingService((WeChatMessageTracingService) ghost.born());
    }

    private List<WeChatMessageHandler> buildMessageHandlers(FilterConfig config) {

        String scanBasePackage = config.getInitParameter(SCAN_BASE_PKG);
        Class<?>[] types = Lookups.lookupMulti(scanBasePackage);
        List<Class<?>> handlerTypes = Langs.filter(Arrays.asList(types), new Predicate<Class<?>>() {
            @Override
            public boolean apply(Class<?> input) {
                return WeChatMessageHandler.class.isAssignableFrom(input);
            }
        });

        return Langs.map(handlerTypes, new Function<Class<?>, WeChatMessageHandler>() {
            @Override
            public WeChatMessageHandler apply(Class<?> type) {
                Class<WeChatMessageHandler> handlerType = (Class<WeChatMessageHandler>) type;
                return Ghost.me(handlerType).born();
            }
        });
    }

    private boolean hasDefaultMessageHandler(List<WeChatMessageHandler> messageHandlers) {
        return Langs.any(messageHandlers, new Predicate<WeChatMessageHandler>() {
            @Override
            public boolean apply(WeChatMessageHandler input) {
                return input.getClass().isAnnotationPresent(DefaultMessageHandler.class);
            }
        });
    }

    private void configDefaultMessageHandler(WeChatMessageDispatcher weChatMessageDispatcher, List<WeChatMessageHandler> messageHandlers) {
        List<WeChatMessageHandler> defaultMessageHandlers = Langs.filter(messageHandlers, new Predicate<WeChatMessageHandler>() {
            @Override
            public boolean apply(WeChatMessageHandler input) {
                return input.getClass().isAnnotationPresent(DefaultMessageHandler.class);
            }
        });

        if (defaultMessageHandlers.size() > 1) {
            throw new MultiDefaultMessageHandlerException(defaultMessageHandlers);
        }

        ((DefaultWeChatMessageDispatcher) weChatMessageDispatcher).setDefaultMessageHandler(defaultMessageHandlers.get(0));
    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        servletResponse.setContentType(WECHAT_MESSAGE_CONTENT_TYPE);

        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        WeChatMessageContext context = WeChatHttpMessageContexts.ofMessageContext(request, response);
        weChatMessageDispatcher.dispatchMessage(context);
    }

    @Override
    public void destroy() {
    }
}
